﻿#region COPYRIGHT© 2009-2012 Phillip Clark. All rights reserved.

// For licensing information see License.txt (MIT style licensing).

#endregion

using System;
using FlitBit.Core.Parallel;

namespace FlitBit.Cache
{
	/// <summary>
	///   Implementation of the ICacheItemResolver interface that demultiplexes
	///   resolution requests.
	/// </summary>
	/// <typeparam name="TKey">key type K</typeparam>
	/// <typeparam name="TRepresentation">representation type R</typeparam>
	public abstract class DemuxCacheItemResolver<TKey, TRepresentation> : CacheItemResolver<TKey, TRepresentation>
	{
		readonly CacheDemuxProducer _producer;

		/// <summary>
		///   Creates a new instance.
		/// </summary>
		protected DemuxCacheItemResolver()
		{
			_producer = new CacheDemuxProducer(this);
		}

		/// <summary>
		///   Tries to resolve an item's representation.
		/// </summary>
		/// <param name="key">the item's key</param>
		/// <param name="value">variable to hold the item's value upon success</param>
		/// <returns>true if successful; otherwise false</returns>
		protected abstract bool DemultiplexedTryResolve(TKey key, out TRepresentation value);

		/// <summary>
		///   Overridden to demultiplex resolve attempts by multiple threads.
		///   If requests are received for the same key within a request period,
		///   all requests wait for the first caller to succeed and get the result
		///   multiplexed back to each concurrent caller.
		/// </summary>
		/// <param name="key">the item's key</param>
		/// <param name="continuation">A continuation called upon completion.</param>
		protected override void PerformTryResolve(TKey key, Continuation<Tuple<ResolutionKinds, TKey, TRepresentation>> continuation)
		{
			try
			{
				_producer.TryConsume(key, (e, res) =>
				{
					if (e != null)
					{
						continuation(e, null);
					}
					else if (res == null)
					{
						continuation(null, default(Tuple<ResolutionKinds, TKey, TRepresentation>));
					}
					else
					{
						continuation(null, new Tuple<ResolutionKinds, TKey, TRepresentation>((ResolutionKinds) (int) res.Item1, key, res.Item2));
					}
				});
			}
			catch (Exception e)
			{
				continuation(e, null);
			}
		}

		internal class CacheDemuxProducer : DemuxProducer<TKey, TRepresentation>
		{
			readonly DemuxCacheItemResolver<TKey, TRepresentation> _parent;

			public CacheDemuxProducer(DemuxCacheItemResolver<TKey, TRepresentation> parent)
			{
				_parent = parent;
			}

			protected override bool ProduceResult(TKey arg, out TRepresentation value)
			{
				return _parent.DemultiplexedTryResolve(arg, out value);
			}
		}
	}
}